package com.peter.spring.framework.context;

import com.peter.spring.framework.annotation.XDAutowired;
import com.peter.spring.framework.annotation.XDController;
import com.peter.spring.framework.annotation.XDService;
import com.peter.spring.framework.aop.XDAopProxy;
import com.peter.spring.framework.aop.XDCglibAopProxy;
import com.peter.spring.framework.aop.XDJdkDynamicAopProxy;
import com.peter.spring.framework.aop.config.XDAopConfig;
import com.peter.spring.framework.aop.support.XDAdvisedSupport;
import com.peter.spring.framework.beans.XDBeanWrapper;
import com.peter.spring.framework.beans.config.XDBeanDefinition;
import com.peter.spring.framework.beans.config.XDBeanPostProcessor;
import com.peter.spring.framework.beans.support.XDBeanDefinitionReader;
import com.peter.spring.framework.beans.support.XDDefaultListableBeanFactory;
import com.peter.spring.framework.core.XDBeanFactory;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Auther: 肖冬
 * @Date: 2020/7/21 17:35
 * @Description: IOC核心容器
 */
public class XDApplicationContext extends XDDefaultListableBeanFactory implements XDBeanFactory {

    private String [] configLoactions;
    private XDBeanDefinitionReader reader;

    //循环依赖的标识，当前正在创建的BeanName，Mark一下
    private Set<String> singletonsCurrentlyInCreation = new HashSet<String>();

    //一级缓存：保存成熟的Bean
    private Map<String,Object> singletonObjects = new HashMap<String, Object>();

    //二级缓存：保存早期的Bean
    private Map<String,Object> earlySingletonObjects = new HashMap<String, Object>();

    //三级缓存（终极缓存
    //单例的IOC容器缓存
    private Map<String,Object> factoryBeanObjectCache = new ConcurrentHashMap<String, Object>();
    //通用的IOC容器
    private Map<String,XDBeanWrapper>  factoryBeanInstanceCache = new ConcurrentHashMap<String, XDBeanWrapper>();

    public XDApplicationContext(String... configLoactions){
        this.configLoactions = configLoactions;
        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Override
    public void refresh() throws Exception{
        //1、定位，定位配置文件
        reader = new XDBeanDefinitionReader(this.configLoactions);

        //2、加载配置文件，扫描相关的类，把它们封装成BeanDefinition
        List<XDBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();

        //3、注册，把配置信息放到容器里面(伪IOC容器)
        doRegisterBeanDefinition(beanDefinitions);

        //4、把不是延时加载的类，有提前初始化
        doAutowrited();

        System.out.println("ioc and dl finished!");
    }

    //只处理非延时加载的情况
    private void doAutowrited() {
        for (Map.Entry<String, XDBeanDefinition> beanDefinitionEntry : super.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            if(!beanDefinitionEntry.getValue().isLazyInit()) {
                try {
                    getBean(beanName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void doRegisterBeanDefinition(List<XDBeanDefinition> beanDefinitions) throws Exception {

        for (XDBeanDefinition beanDefinition: beanDefinitions) {
            if(super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
                throw new Exception("The “" + beanDefinition.getFactoryBeanName() + "” is exists!!");
            }
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
        }
        //到这里为止，容器初始化完毕
    }
    public Object getBean(Class<?> beanClass) throws Exception {
        return getBean(beanClass.getName());
    }

    //依赖注入，从这里开始，通过读取BeanDefinition中的信息
    //然后，通过反射机制创建一个实例并返回
    //Spring做法是，不会把最原始的对象放出去，会用一个BeanWrapper来进行一次包装
    //装饰器模式：
    //1、保留原来的OOP关系
    //2、我需要对它进行扩展，增强（为了以后AOP打基础）
    public Object getBean(String beanName) throws Exception {

        XDBeanDefinition XDBeanDefinition = this.beanDefinitionMap.get(beanName);
        Object instance = null;

        //循环依赖的终结条件
        Object singleton = getSingleton(beanName,XDBeanDefinition);
        if(singleton != null){ return  singleton; }

        //标记bean正在创建
        if(!singletonsCurrentlyInCreation.contains(beanName)){
            singletonsCurrentlyInCreation.add(beanName);
        }

        //这个逻辑还不严谨，自己可以去参考Spring源码
        //工厂模式 + 策略模式
        XDBeanPostProcessor postProcessor = new XDBeanPostProcessor();

        postProcessor.postProcessBeforeInitialization(instance,beanName);

        instance = instantiateBean(beanName,XDBeanDefinition);

        //input to singletonObjects cache
        this.singletonObjects.put(beanName,instance);

        //3、把这个对象封装到BeanWrapper中
        XDBeanWrapper beanWrapper = new XDBeanWrapper(instance);

        //4、把BeanWrapper存到IOC容器里面
//        //1、初始化

//        //class A{ B b;}
//        //class B{ A a;}
//        //先有鸡还是先有蛋的问题，一个方法是搞不定的，要分两次

        //2、拿到BeanWraoper之后，把BeanWrapper保存到IOC容器中去
        this.factoryBeanInstanceCache.put(beanName,beanWrapper);

        postProcessor.postProcessAfterInitialization(instance,beanName);

//        //3、注入
        populateBean(beanName,new XDBeanDefinition(),beanWrapper);


        return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
    }

    private Object getSingleton(String beanName, XDBeanDefinition beanDefinition) {

        //先去一级缓存里面拿
        Object bean = singletonObjects.get(beanName);
        //如果一级缓存中没有，但是又有创建标识，说明就是循环依赖
        if(bean == null && singletonsCurrentlyInCreation.contains(beanName)){
            bean = earlySingletonObjects.get(beanName);

            //如果二级缓存也没有，那就从三级缓存中拿
            if(bean == null){
                bean = instantiateBean(beanName,beanDefinition);

                //将创建出来的对象重新放入到二级缓存中
                earlySingletonObjects.put(beanName,bean);
            }
        }

        return bean;
    }

    private void populateBean(String beanName, XDBeanDefinition XDBeanDefinition, XDBeanWrapper XDBeanWrapper) {
        Object instance = XDBeanWrapper.getWrappedInstance();

//        XDBeanDefinition.getBeanClassName();

        Class<?> clazz = XDBeanWrapper.getWrappedClass();
        //判断只有加了注解的类，才执行依赖注入
        if(!(clazz.isAnnotationPresent(XDController.class) || clazz.isAnnotationPresent(XDService.class))){
            return;
        }

        //获得所有的fields
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if(!field.isAnnotationPresent(XDAutowired.class)){ continue;}

            XDAutowired autowired = field.getAnnotation(XDAutowired.class);

            String autowiredBeanName =  autowired.value().trim();
            if("".equals(autowiredBeanName)){
                autowiredBeanName = field.getType().getName();
            }

            //强制访问
            field.setAccessible(true);

            try {
                //为什么会为NULL，先留个坑
//                if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){
//                    continue;
//                }
//                if(instance == null){
//                    continue;
//                }
//                field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
                field.set(instance,getBean(autowiredBeanName));
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }

    private Object instantiateBean(String beanName, XDBeanDefinition XDBeanDefinition) {
        //1、拿到要实例化的对象的类名
        String className = XDBeanDefinition.getBeanClassName();

        //2、反射实例化，得到一个对象
        Object instance = null;
        try {
//            XDBeanDefinition.getFactoryBeanName()
            //假设默认就是单例,细节暂且不考虑，先把主线拉通
            if(this.factoryBeanObjectCache.containsKey(className)){
                instance = this.factoryBeanObjectCache.get(className);
            }else {
                Class<?> clazz = Class.forName(className);
                instance = clazz.newInstance();

                XDAdvisedSupport config = instantionAopConfig(XDBeanDefinition);
                config.setTargetClass(clazz);
                config.setTarget(instance);

                //符合PointCut的规则的话，创建代理对象
                if(config.pointCutMatch()) {
                    instance = createProxy(config).getProxy();
                }

                this.factoryBeanObjectCache.put(className,instance);
                this.factoryBeanObjectCache.put(XDBeanDefinition.getFactoryBeanName(),instance);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        return instance;
    }

    private XDAopProxy createProxy(XDAdvisedSupport config) {

        Class targetClass = config.getTargetClass();
        if(targetClass.getInterfaces().length > 0){
            return new XDJdkDynamicAopProxy(config);
        }
        return new XDCglibAopProxy(config);
    }

    private XDAdvisedSupport instantionAopConfig(XDBeanDefinition XDBeanDefinition) {
        XDAopConfig config = new XDAopConfig();
        config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
        return new XDAdvisedSupport(config);
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new  String[this.beanDefinitionMap.size()]);
    }

    public int getBeanDefinitionCount(){
        return this.beanDefinitionMap.size();
    }

    public Properties getConfig(){
        return this.reader.getConfig();
    }
}












